package com.plian.system.service.fr.report.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.plian.Tools.CollectionUtil;
import com.plian.Tools.StringPool;
import com.plian.Tools.StringUtil;
import com.plian.Tools.pagedata.PageData;
import com.plian.system.config.exception.MyRuntimeException;
import com.plian.system.constant.sys.FormStatusConstant;
import com.plian.system.constant.sys.FormTypeConstant;
import com.plian.system.constant.yth.YthConstant;
import com.plian.system.dao.fr.authorize.BudgetAuthorizeDao;
import com.plian.system.dao.fr.financeindicators.FinanceIndicatorsDao;
import com.plian.system.dao.fr.gsalaryslip.GsalarySlipDao;
import com.plian.system.entity.fr.authorize.AuthorizeLink;
import com.plian.system.entity.fr.authorize.AuthorizeValue;
import com.plian.system.entity.fr.report.*;
import com.plian.system.entity.sys.Org;
import com.plian.system.mapper.fr.report.ReportMapper;
import com.plian.system.model.CommonCode;
import com.plian.system.model.ListResult;
import com.plian.system.service.fr.authorize.IAuthorizeLinkService;
import com.plian.system.service.fr.authorize.IAuthorizeValueService;
import com.plian.system.service.fr.report.*;
import com.plian.system.service.sys.orgzation.IOrgService;
import com.plian.system.service.yth.helper.YthExcelUtils;
import com.plian.system.vo.fr.report.ReportTableConfigVO;
import com.plian.system.vo.yth.YthExcelInfoVO;
import com.plian.system.vo.yth.YthResult;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.plian.system.constant.sys.TableTypeCode.DIFFERENCE_TABLE_TYPE;
import static com.plian.system.constant.yth.YthConstant.EXCEL_MERGE;
import static com.plian.system.constant.yth.YthConstant.EXCEL_SINGLE;

/**
 * @description:
 * @author: gangan
 * @create: 2022-01-07 16:43
 */
@Slf4j
@Service
@AllArgsConstructor
public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements IReportService {

    private GsalarySlipDao gsalarySlipDao;

    private BudgetAuthorizeDao budgetAuthorizeDao;

    private IReportVersionService reportVersionService;

    private IAuthorizeLinkService authorizeLinkService;

    private IReportAuthorizeRelationService reportAuthorizeRelationService;

    private IAuthorizeValueService authorizeValueService;

    private IReportLinkService reportLinkService;

    private IOrgService orgService;

    private FinanceIndicatorsDao financeIndicatorsDao;

    private IReportColumnService reportColumnService;

    private IReportColumnValueService reportColumnValueService;

    private IReportTableConfigService reportTableConfigService;

    @Resource
    private Executor ttlExecutor;

    private final String C2 = "c2";
    private final int START_CHANGE_LINE_NUMBER = 8;

    private static final String VERSION = "version";

    private static final String SHEET_ID = "orgId";

    private static final String IS_CHANGE = "isChange";

    private static final String SHEET_NAME = "name";

    private final List<String> NOT_UPDATE_STATUS = Stream
            .of(FormStatusConstant.FLOW + "", FormStatusConstant.FINISH + "").collect(Collectors.toList());

    @Override
    public ListResult<PageData> findAuthorizeValue(PageData pageData) {
        try {
            PageData gsalaryTable = gsalarySlipDao.findZbById(pageData);
            String orgId = pageData.getString("orgId");
            String year = gsalaryTable.getString("year");
            String version = reportVersionService.getVersion(year);
            pageData.put("version", version);
            gsalaryTable.put("year", Integer.valueOf(year) - 1);
            PageData authorizeInfo = budgetAuthorizeDao.findzb(gsalaryTable);
            if (authorizeInfo == null){
                ListResult<PageData> result = new ListResult<>(CommonCode.FAIL);
                result.setMessage("此年度未填报相关数据");
                return result;
            }
            List<AuthorizeLink> authorizeLinks = authorizeLinkService.list(new LambdaQueryWrapper<AuthorizeLink>()
                    .eq(AuthorizeLink::getId, authorizeInfo.getString("id")));
            Map<String, String> orgLinkMap = authorizeLinks.stream().collect(Collectors.toMap(AuthorizeLink::getOrgId, AuthorizeLink::getLinkId, (a, b) -> a));
            List<ReportAuthorizeRelation> reportAuthorizeRelations =  reportAuthorizeRelationService
                    .list(new LambdaQueryWrapper<ReportAuthorizeRelation>()
                            .eq(ReportAuthorizeRelation::getReportTableName, orgId)
                            .eq(ReportAuthorizeRelation::getVersion, version)
                            .orderByAsc(ReportAuthorizeRelation::getReportLines));
            if (CollectionUtil.isEmpty(reportAuthorizeRelations)){
                ListResult<PageData> result = new ListResult<>(CommonCode.FAIL);
                result.setMessage("预算表和审定表关系配置有问题，请联系管理员!");
                return result;
            }
            LambdaQueryWrapper<AuthorizeValue> valueQueryWrapper = new LambdaQueryWrapper<>();
            int count = 0;
            for (int i = 0; i< reportAuthorizeRelations.size(); i++){
                ReportAuthorizeRelation reportAuthorizeRelation = reportAuthorizeRelations.get(i);
                if (!orgLinkMap.containsKey(reportAuthorizeRelation.getAuthorizeTableName())){
                    continue;
                }
                String linkId = orgLinkMap.get(reportAuthorizeRelation.getAuthorizeTableName());
                valueQueryWrapper.or(wrapper -> wrapper.eq(AuthorizeValue::getLinkId, linkId)
                        .eq(AuthorizeValue::getColumnId, reportAuthorizeRelation.getAuthorizeColumnId())
                        .eq(AuthorizeValue::getOrderNumber, reportAuthorizeRelation.getAuthorizeLines()));
                count++;
            }


            if (count == 0){
                ListResult<PageData> result = new ListResult<>(CommonCode.FAIL);
                result.setMessage("此年度未填报相关数据");
                return result;
            }
            List<AuthorizeValue> values = authorizeValueService.list(valueQueryWrapper);
            Map<String, AuthorizeValue> authorizeValueMap = values.stream().collect(Collectors.toMap(value -> value.getColumnId() + StringPool.UNDERSCORE + value.getOrderNumber(), value -> value, (a, b) -> a));


//            int startChangeLineNumber = tableInfo.getInteger("startChangeLineNumber");
            List<PageData> results = new ArrayList<>();
            if (START_CHANGE_LINE_NUMBER > 0){
                for (int i = 1; i < START_CHANGE_LINE_NUMBER; i++){
                    PageData initMap = new PageData();
                    initMap.put(C2, "");
                    results.add(initMap);
                }
            }
            int lastIndex = 0;
            for (ReportAuthorizeRelation reportAuthorizeRelation : reportAuthorizeRelations){
                int index = Integer.valueOf(reportAuthorizeRelation.getReportLines());
                if (lastIndex != 0 &&  index > lastIndex + 1){
                    for (int i = lastIndex + 1; i < index; i++){
                        PageData initMap = new PageData();
                        initMap.put(C2, "");
                        results.add(initMap);
                    }
                }
                lastIndex = index;


                PageData valueMap = new PageData();
                valueMap.put(C2, "");
                String key = reportAuthorizeRelation.getAuthorizeColumnId() + "_" + reportAuthorizeRelation.getAuthorizeLines();
                if (authorizeValueMap.containsKey(key)){
                        AuthorizeValue authorizeValue = authorizeValueMap.get(key);
                        valueMap.put(C2, authorizeValue.getReplyValue());
                }
                results.add(valueMap);
            }

            ListResult<PageData> result = new ListResult<>(CommonCode.SUCCESS);
            result.setList(results);
            return result;
        }catch (Exception e){
            log.error(ExceptionUtils.getFullStackTrace(e));
            throw new RuntimeException("系统错误");
        }


    }

    @Override
    public List<Report> findFinishByYear(int year) {
        List<Report> reportList = list(Wrappers.<Report>lambdaQuery().eq(Report::getYear, year)
                .eq(Report::getStatus, FormStatusConstant.FINISH)
                .ne(Report::getFhTableTypeCode, DIFFERENCE_TABLE_TYPE));
        if (CollectionUtil.isEmpty(reportList)){
            return new ArrayList<>();
        }
        List<ReportLink> reportLinks = reportLinkService.list(Wrappers.<ReportLink>lambdaQuery().in(ReportLink::getId, reportList.stream().map(Report::getId).collect(Collectors.toList())));
        Map<String, List<ReportLink>> linkMap = reportLinks.stream().collect(Collectors.groupingBy(ReportLink::getId));
        for (Report report : reportList){
            if (linkMap.containsKey(report.getId())){
                report.setReportLinkList(linkMap.get(report.getId()));
            }
        }
        return reportList;
    }

    @Override
    public String ythExcelUpload(YthExcelInfoVO ythExcelInfoVO, MultipartFile multipartFile) {
        if (multipartFile == null){
            return JSON.toJSONString(YthResult.builder().failReason("文件为空!"));
        }
        Org org = orgService.getByDataSourceId(YthConstant.DATA_NINGBO, ythExcelInfoVO.getOrgId());
        if (org == null){
            return JSON.toJSONString(YthResult.builder().failReason(StringUtil.format("orgId = {} ,该单位不存在!", ythExcelInfoVO.getOrgId())));
        }
        int summaryCode = orgService.getSummaryCode(org);
        if (ythExcelInfoVO.getValueType() == EXCEL_MERGE){
            summaryCode = summaryCode - 1;
        }
        if ("3".equals(org.getTypeCode())){
            if (ythExcelInfoVO.getValueType() == EXCEL_SINGLE || ythExcelInfoVO.getValueType() == EXCEL_SINGLE){
                summaryCode = summaryCode + 1;
            }
        }

        int year = Integer.valueOf(ythExcelInfoVO.getReportTime());

        String tableTypeCode = YthExcelUtils.valueTypeToTableType(ythExcelInfoVO.getValueType());
        Report oldReport = getOne(Wrappers.<Report>lambdaQuery()
                .eq(Report::getUnit, org.getId())
                .eq(Report::getYear, year)
                .eq(Report::getSummaryCode, summaryCode)
                .eq(Report::getFhTableTypeCode, tableTypeCode));
        if (oldReport != null && NOT_UPDATE_STATUS.contains(oldReport.getStatus())){
            return JSON.toJSONString(YthResult.builder()
                    .failReason(StringUtil.format("orgId = {} ,valueType = {}, reportTime = {},该单据已上报!",
                            ythExcelInfoVO.getOrgId(), ythExcelInfoVO.getValueType(), ythExcelInfoVO.getReportTime())));
        }


        String version = reportVersionService.getVersion(year + "");
        PageData budgetPd = new PageData();
        budgetPd.put(VERSION, version);
        List<PageData> sheetList;
        try {
            sheetList = financeIndicatorsDao.findbudgettype(budgetPd);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (CollectionUtil.isEmpty(sheetList)){
            return JSON.toJSONString(YthResult.builder().failReason("获取预算配置失败!"));
        }

        List<ReportLink> reportLinks = new ArrayList<>();
        if (oldReport == null){
            oldReport = new Report();
            oldReport.setStatus(FormStatusConstant.UNREVIEWED + "");
            oldReport.setSummaryCode(summaryCode);
            oldReport.setFhTableTypeCode(tableTypeCode);
            oldReport.setUnit(org.getId());
            oldReport.setUnitName(org.getName());
            oldReport.setYear(year);
            oldReport.setApplicantName(ythExcelInfoVO.getInformant());
            oldReport.setChecked("0");
            oldReport.setDiffChecked("0");
            oldReport.setIsPassed("0");
            oldReport.setFormType(FormTypeConstant.GSALARY_SLIP_FORM + "");
            save(oldReport);

            for (PageData sheet : sheetList){
                ReportLink reportLink = new ReportLink();
                reportLink.setId(oldReport.getId());
                reportLink.setOrgId(sheet.getString(SHEET_ID));
                reportLink.setStatus("0");
                reportLinks.add(reportLink);
            }
            reportLinkService.saveBatch(reportLinks);
        }else {
            reportLinks = reportLinkService.list(Wrappers.<ReportLink>lambdaQuery()
                    .eq(ReportLink::getId, oldReport.getId()));
        }
        List<ReportColumnValue> reportColumnValues = excelToValue(reportLinks, sheetList, multipartFile);
        if (CollectionUtil.isNotEmpty(reportColumnValues)){
            reportColumnValueService.remove(Wrappers.<ReportColumnValue>lambdaQuery()
                    .in(ReportColumnValue::getLinkId, reportLinks.stream().map(ReportLink::getLinkId).collect(Collectors.toList())));
            reportColumnValueService.saveBatch(reportColumnValues);
        }
        return JSON.toJSONString(YthResult.builder().successIds(Stream.of(ythExcelInfoVO.getFormId()).collect(Collectors.toList())));
    }

    @Override
    public List<Map<String, Object>> getAllValue(String year, String id) {
        ReportTableConfigVO reportTableConfigVO = reportTableConfigService.getTableConfig(year);
        JSONArray tableNames = reportTableConfigVO.getTableNames();
        JSONArray colNames = reportTableConfigVO.getColNames();
        List<Map<String, Object>> results = new LinkedList<>();
        for (int i = 0; i < tableNames.size(); i++){
            String tableName = tableNames.getString(i);
            Map<String, Object> data = new HashMap<>();
            List<PageData> valueList = getValueList(id, tableName, colNames.getObject(i, List.class));
            data.put(tableName, valueList);
            results.add(data);
        }

        return results;
    }

    private List<PageData> getValueList(String id, String tableName, List<String> columnIdList){
        if (CollectionUtil.isEmpty(columnIdList)){
            return null;
        }
        PageData pd = new PageData();
        String value = "";
        for (int i = 0; i < columnIdList.size(); i++) {
            String columnId = columnIdList.get(i);
            if ("".equals(value)) {
                value += " max(CASE c.columnId WHEN '" + columnId + "' THEN v.`values` ELSE '' END) AS " + "c" + i;
            } else {
                value += " ,max(CASE c.columnId WHEN '" + columnId + "' THEN v.`values` ELSE '' END) AS " + "c" + i;
            }
        }
        pd.put("id", id);
        pd.put("orgId", tableName);
        pd.put("vaues", value);
        List<PageData> valueList = null;
        try {
            valueList = gsalarySlipDao.findvalue(pd);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return valueList;
    }

    private List<ReportColumnValue> excelToValue(List<ReportLink> reportLinks, List<PageData> sheetList, MultipartFile multipartFile){
        Map<String, String> linkSheetIdMap = reportLinks.stream().collect(Collectors.toMap(ReportLink::getOrgId, ReportLink::getLinkId));
        Map<String, PageData> sheetNameMap = sheetList.stream().collect(Collectors.toMap(sheet -> sheet.getString(SHEET_NAME), sheet -> sheet));
        Map<String, String> columnIdMap = reportColumnService.list(Wrappers.<ReportColumn>lambdaQuery()
                        .in(ReportColumn::getOrgId, sheetList.stream().map(sheet -> sheet.getString(SHEET_ID)).collect(Collectors.toList())))
                .stream().collect(Collectors.toMap(reportColumn -> reportColumn.getOrgId() + StringPool.DASH + reportColumn.getOrderNumber(), ReportColumn::getColumnId));
        try {
            InputStream stream = multipartFile.getInputStream();
            //接收文件流
            //将流给工作簿
            Workbook book = null;
            try {
                book = new HSSFWorkbook(stream);
            } catch (Exception ex) {
                ex.printStackTrace();
                // 解决read error异常
                stream = multipartFile.getInputStream();
                book = new XSSFWorkbook(stream);
            }
            if (book == null){
                throw new MyRuntimeException("excel文件解析失败");
            }

            List<ReportColumnValue> reportColumnValues = new ArrayList<>();

            for (int i = 0; i < book.getNumberOfSheets(); i++) {
                Sheet sheet = book.getSheetAt(i);
                String sheetName = sheet.getSheetName();
                if (!sheetNameMap.containsKey(sheetName)){
                    continue;
                }
                PageData sheetPageData =  sheetNameMap.get(sheetName);
                String sheetId = sheetPageData.getString(SHEET_ID);
                if (!linkSheetIdMap.containsKey(sheetId)){
                    continue;
                }
                String linkId = linkSheetIdMap.get(sheetId);
                //遍历行
                for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                    Row row = sheet.getRow(rowNum);
                    if (null == row) {
                        continue;
                    }
                    //遍历列
                    for (int cellNum = 0; cellNum <= row.getLastCellNum(); cellNum++) {
                        ReportColumnValue reportColumnValue = new ReportColumnValue();
                        String key = sheetId + StringPool.DASH + (cellNum + 1);
                        if (!columnIdMap.containsKey(key)){
                            continue;
                        }
                        reportColumnValue.setColumnId(columnIdMap.get(key));
                        reportColumnValue.setOrderNumber(rowNum + 1);
                        reportColumnValue.setLinkId(linkId);
                        Cell cell = row.getCell(cellNum);
                        if (null == cell) {
                            continue;
                        }
                        reportColumnValue.setValues(YthExcelUtils.getValue(cell, sheetPageData.getString(IS_CHANGE)));
                        reportColumnValues.add(reportColumnValue);
                    }
                }
            }
            book.close();
            stream.close();
            return reportColumnValues;
        } catch (IOException e) {
            throw new MyRuntimeException(ExceptionUtils.getFullStackTrace(e));
        }
    }
}
